package ru.fadeev.crammer.phb;

import java.sql.*;
import java.util.Random;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.InitialContext;
import javax.sql.DataSource;


public class PhrasesBankImpl {
	public PhrasesBatch getPhrases(WordsBatch wordsBatch, int phraseLimit)
	{	
		if(phraseLimit <= 0) {
			phraseLimit = 1000;
		}
		Vector<String> result = new Vector<String>();
		Vector<Integer> ids = new Vector<Integer>();
		String[] words = wordsBatch.getWords();
		try {
			Connection connection = getConnection();
			for(int i = 0; i < words.length; i++) {
				String word = words[i];
				if(word.length() < 3) {
					continue;
				}
				Statement statement = connection.createStatement();
				String s = word.trim().replaceAll("\\s+", " & ");
				
				//Getting total number of phrases for the word
				String countQuery = "SELECT COUNT(*) " +
					"FROM PHRASE " +
					"WHERE to_tsquery('" + s + "') @@ phrase_tsvector";
				ResultSet rs = statement.executeQuery(countQuery);
				rs.next();				
				int phraseCount = rs.getInt(1);
				int batchSize = phraseLimit*2;
				int batchCount = (phraseCount-1)/batchSize + 1;
				Vector<Integer> batchIndices = new Vector<Integer>();			
				for(int j = 0; j < batchCount; j++) {
					batchIndices.add(new Integer(j));				
				}
				int addedPhraseCount = 0;
				
				Random random = new Random(System.currentTimeMillis());
				while((addedPhraseCount < phraseLimit) 
						&& (batchIndices.size() > 0)) {
					int randomIndex = random.nextInt(batchIndices.size());
					int batchIndex = batchIndices.remove(randomIndex).intValue();
					
					String query = "SELECT ID, PHRASE_TEXT " +
	    				"FROM PHRASE " +
	    				"WHERE to_tsquery('" + s + "') @@ phrase_tsvector " +
						"LIMIT " + batchSize + 
						" OFFSET " + (batchIndex * batchSize);
					
					rs = statement.executeQuery(query);
	
					while(rs.next()) {
						String phraseText = rs.getString(2);
			    		if(isRightPhrase(word, phraseText)) {
			    			int phraseId = rs.getInt(1);
			    			int j = 0;	
							for(; j < ids.size(); j++) {
								Integer id = (Integer)ids.elementAt(j);
								if(id == phraseId) {
									break;
								} else if(id > phraseId) {
									ids.insertElementAt(new Integer(phraseId), j);
					    			result.addElement(phraseText);
					    			addedPhraseCount++;
						    		break;
								}
							}
							if(j == ids.size()) {
								ids.addElement(new Integer(phraseId));
				    			result.addElement(phraseText);
				    			addedPhraseCount++;
							}
							if(addedPhraseCount == phraseLimit) {
								break;
							}
						}
					}
				}
				statement.close();	
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		PhrasesBatch phrasesBatch = new PhrasesBatch();
		String[] phrases = new String[result.size()];
		System.arraycopy(result.toArray(), 0, phrases, 0, result.size());
		phrasesBatch.setPhrases(phrases);
		phrasesBatch.setLanguage("EN");
		return phrasesBatch;
	}
	
	
	private Connection getConnection() throws Exception {
		InitialContext ctx = new InitialContext();
		DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/DataSource");
		return ds.getConnection();
	}
	
	/**
	 * Checks if the word is included in the phrase  
	 * @param eng
	 * @param phrase
	 * @return
	 */
    private boolean isRightPhrase(String eng, String phrase) {
    	int wordsNumber = eng.split("[\\s]+").length;
    	String regExp;
    	if(wordsNumber == 1) {
        	regExp = "\\b" + eng.trim() + "\\b";
    	} else {
    		//between two parts of word we allow one or two another words 
    		regExp = "\\b" + eng.trim().replaceAll("[\\s]+", 
    				"(([\\\\s]+)|([\\\\s]+[\\\\w]+[\\\\s]+)" 
    				+ "|([\\\\s]+[\\\\w]+[\\\\s]+[\\\\w]+[\\\\s]+))") + "\\b";
    	}
    	
    	Pattern p = Pattern.compile(regExp, Pattern.CASE_INSENSITIVE);
    	Matcher matcher = p.matcher(phrase);
    	if(matcher.find()) {
    		return true;
    	}
    	return false;
    }
}
